#!/usr/bin/env python
# _*_ coding:utf-8 _*_

'''
'''
import os, sys
curdir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(curdir)
import sr_yaml
import re
import shlex
import numpy as np
import SR_Context
import StrUtils
import PandasUtils
import CommonUtils
import ImportCode
import ActionTemplate
import RuleTemplate

logger = CommonUtils.getLogger('GrammerDict2Py')

def processMacros(_src, statement, getActionExpr='', xy=''):
    '''
    rewrite_variables

    This function takes an atom-mapped reaction and reassigns
    the atom-mapping labels (numbers) from left to right, once
    that transform has been canonicalized.

    $.variable --> df["variable"]
    '''

    def lambda_match(_src, replacements):
        varName = replacements.pop(0)
        if CommonUtils.getDict(varName, _src.get_value('require'), None) != None:
            return '_src.get_value("require")["' + varName + '"]'
        return ('df["' + varName + '"]')

    statement, mark = getDebugMark(_src, statement)

    reg_str = r'\$\.\w*'
    status, s = StrUtils.getStatementContent(statement, separator='//')
    if (status != 0):
        return 1, 'ERROR:【' + mark + ': ' + s + '】 ' + statement

    statement = s

    if getActionExpr:
        status, statement = StrUtils.getStatementContent(statement, getComment=True, valNone=False, separator=';')
        if not statement:
            return 2, ''

        if (statement[0] == '$'):
            plan, main, actor, final, gfinal, merge = ActionTemplate.get_template(_src, statement, getActionExpr, xy=xy)
            if not actor:
                return 1, 'ERROR:【' + mark + '】 '+ s + '\n' + statement
            statement = actor
            sr_plan(_src, plan)
            sr_main(_src, main)
            sr_final(_src, final)
            sr_gfinal(_src, gfinal)
            sr_merge(_src, merge)
    else:
        status, statement = StrUtils.getStatementContent(statement, separator=';')

    all_labels = re.findall(reg_str, statement)

    # Define list of replacements which matches all_labels *IN ORDER*
    replacements = []
    for label in all_labels: # keep in order! this is important
        replacements.append(label[2:])

    # Perform replacements in order
    transform_newmaps = re.sub(reg_str, lambda match: lambda_match(_src, replacements), statement)

    new_statement = transform_newmaps.replace('df[""]', '_src.data')
    if (new_statement == '' or new_statement == 'pass'):
        new_statement = '_p_a_s_s__'
    status, s = ImportCode.sr_CheckCode(_src, new_statement)
    if (status != 0):
        if mark:
            return 3, 'ERROR:【' + mark + '】 '+ s + '===>' + statement
        else:
            return 3, s + '===>' + statement

    return 0, new_statement


def getDebugMark(_src, statement):
    if isinstance(statement, list):
        retList = []
        retMark = ''
        for item in statement:
            s, mark = getDebugMark(_src, item)
            retList.append(s)
            if not retMark:
                retMark = mark
        return retList, retMark

    if (_src.debug and (statement[0] == '行')):
        pos, mark = StrUtils.getToken(statement, 0)
        if (pos > 0) and (pos+1 < len(statement)):
            statement = statement[pos+1:]
        return statement, mark

    return statement, ''


def dict_generator(indict, pre=None):
    '''
    https://blog.csdn.net/qq_17550379/article/details/80276477
    输出JSON字典，并且对其中的每个元素，要输出它的所有子节点
    '''
    pre = pre[:] if pre else []
    if isinstance(indict, dict):
        for key, value in indict.items():
            key = StrUtils.removeNoise(key)
            if isinstance(value, dict):
                if len(value) == 0:
                    yield pre+[key, '{}']
                else:
                    for d in dict_generator(value, pre + [key]):
                        yield d
            elif isinstance(value, list):
                if len(value) == 0:
                    yield pre+[key, '[]']
                else:
                    for v in value:
                        for d in dict_generator(v, pre + [key]):
                            yield d
            elif isinstance(value, tuple):
                if len(value) == 0:
                    yield pre+[key, '()']
                else:
                    for v in value:
                        for d in dict_generator(v, pre + [key]):
                            yield d
            else:
                yield pre + [key, value]
    else:
        yield indict


def itemCmp(item0, item1):
    len_ = min(len(item0), len(item1))
    if (len_ <= 0):
        return 0
    for i in range(len_):
        status, item0_ = StrUtils.getStatementContent(item0[i])
        status, item1_ = StrUtils.getStatementContent(item1[i])
        if item0_ != item1_:
            return i
    return i+1


def isConditionY(key):
    status, key = StrUtils.getStatementContent(key)
    matchObj = re.match(r'^y|row|\$y$', key, re.I)
    if matchObj:
        return True
    return False


def isConditionX(key):
    status, key = StrUtils.getStatementContent(key)
    matchObj = re.match(r'^x|column|\$x$', key, re.I)
    if matchObj:
        return True
    return False


def checkIdentifier(tokens):
    '''
    检测“逗号分隔的字段名”的合法性
    tokens = "  bls+++ah, lots  ,  of ,  spac  es, here "
    '''
    identifier = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE)
    pattern = re.compile(r"^\s+|\s*,\s*|\s+$")
    for x in pattern.split(tokens):
        if x:
            if not re.match(identifier, x):
                return False
    return True


def getConditionVar(_src, type, key):

    key, mark = getDebugMark(_src, key)

    status, key = StrUtils.getStatementContent(key)
    type = '^' + type + r'\s*((\d*)|(groupby|dict|rule|dynamic|range))\s*$'
    matchObj = re.match(type, key, re.I)
    if matchObj:
        str_ = matchObj.group(1).lower()
        if (len(str_) == 0):
            if ('y' == type):
                return _src.numY, mark
            if ('x' == type):
                return _src.numX, mark
            else:
                return -1, mark
        else:
            if ('groupby' == str_):
                return -2, mark
            if ('dict' == str_):
                return -3, mark
            if ('rule' == str_):
                return -4, mark
            if ('dynamic' == str_):
                return -5, mark
            if ('range' == str_):
                return -6, mark
        return int(str_), mark

    logger.warning('【' + mark + '】' + 'X(/Y)条件设置时，可能出错了，正确写法是：X(/Y) 行列序数(0,1,2...) 或: X(/Y) groupby 或: X(/Y) dict')
    return -1, mark


def printIitems(_src, item_0, item_s, item, indent):
    '''
    :param _src:
    :param item_0:
    :param item_s:  整行表达式，全的
    :param item:    本行要处理的，缩进部分已经去掉
    :param indent:
    :return:
    '''
    retStr = ''
    g_status = 0            #累积性错误记录

    lenBefore = len(item) - 2
    for j in range(lenBefore):

        status, new_statement = processMacros(_src, str(item[j]))
        g_status -= status
        retStr += (indent + 'if ' + new_statement + ':') + '\n'
        indent += '    '

    if isConditionY(item_0):
        ivar, mark = getConditionVar(_src, 'y', item[-2])
        if (ivar >= 0):
            # y: ...... y1 // 注释 // 数据项标识: 条件表达式 ; 动作表达式
            if (ivar >= _src.numY):
                _src.numY = ivar + 1

            s, mark = getDebugMark(_src, item_s[:-1])
            _src.titlesY[ivar] = s

            status, actionStatement = StrUtils.getStatementContent(item[-1], getComment=True, valNone=False, separator=';')

            status, actionStatement = processMacros(_src, item[-1], getActionExpr=str(ivar), xy='y')
            if (status == 2):
                actionStatement = 'cu.stateY(_src,' + str(ivar) + ')'
            elif (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''
            elif (status == 3):
                logger.error('【' + mark + '】动作表达式错：' + item[-1] + '\n' + actionStatement)
                return 2, ''
            else:
                actionStatement = ('\n    ' + indent).join(actionStatement.split('\n'))

            status, new_statement = processMacros(_src, str(item[-1]))
            if (new_statement == '_p_a_s_s__'):
                retStr += (indent + 'pass\n')
            else:
                g_status -= status
                retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + actionStatement) + '\n'
        elif (ivar == -2):
            # y: ...... groupby // 注释 // 数据项标识: groupbyExpr ; 动作表达式
            s, mark = getDebugMark(_src, item_s[-1])
            groupby = s

            # 去掉//后的注释
            status, groupby_ = StrUtils.getStatementContent(groupby, getComment=False, valNone=False, separator='//')

            status, groupby = StrUtils.getStatementContent(groupby_, getComment=False, valNone=False, separator=';')
            status, actionStatement = StrUtils.getStatementContent(groupby_, getComment=True, valNone=False, separator=';')
            if (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''

            if not checkIdentifier(groupby):
                logger.error('【' + mark + '】分组（groupby）字段列表描述错：' + groupby)
                return 1, ''

            status, s = PandasUtils.index2Statements(_src, 'y', indent, item_s, groupby)
            if (status != 0):
                logger.error('【' + mark + '】分组（groupby）字段列表描述错：' + groupby + '  ' + str(s))
                return 3, ''
            retStr += s
        elif (ivar in (-3, -6)):
            # y: ...... dict // 注释 // 数据项标识: dictExpr ; 动作表达式
            # y: ...... range // 注释 // 数据项标识: rangeExpr ; 动作表达式
            if (ivar == -3):
                key_or_range = 'k'
            else:
                key_or_range = 'r'
            dict0, mark = getDebugMark(_src, item_s[-1])

            # 去掉//后的注释
            status, dict0_ = StrUtils.getStatementContent(dict0, getComment=False, valNone=False, separator='//')

            status, dict0 = StrUtils.getStatementContent(dict0_, getComment=False, valNone=False, separator=';')
            status, actionStatement = StrUtils.getStatementContent(dict0_, getComment=True, valNone=False, separator=';')
            if (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''

            try:
                groupby_dict = eval(dict0)
            except:
                # 检查这个dict后是不是引用的变量
                try:
                    requireDict = _src.get_value('require')
                    # y dict: dictExpr   # dictExpr的值是这时现算出来的
                    s = eval(dict0, requireDict)
                    if isinstance(s, str):
                        status, s = PandasUtils.justProcessMacros(_src, s)
                        if status:
                            logger.error('【' + mark + '】分组（dict）描述错：' + dict0)
                            return 1, ''
                        groupby_dict = eval(s)
                    elif isinstance(s, dict):
                        groupby_dict = s
                    elif isinstance(s, (list, tuple)):
                        # 通过这种方式把表达式返回的值是tuple类型
                        # s = eval("name, name.update({'a':1})", requireDict)
                        groupby_dict = None
                        for x in s:
                            if isinstance(x, dict):
                                groupby_dict = x
                                break

                        if not groupby_dict:
                            groupby_dict = PandasUtils.convertList2Dict(s)

                        if not groupby_dict:
                            logger.error('【' + mark + '】分组（dict）描述数据类型错：' + dict0)
                            return 1, ''
                    else:
                        logger.error('【' + mark + '】分组（dict）描述数据类型错：' + dict0)
                        return 1, ''
                except (BaseException) as exc:
                    logger.error('【' + mark + '】分组（dict）描述错：' + dict0)
                    return 1, ''

            status, s = PandasUtils.dictOrRule2Statements(_src, 'y', indent, item_s, groupby_dict, rightIsExpr=False,
                                                            actionStatement=actionStatement, key_or_range=key_or_range)
            if (status != 0):
                logger.error('【' + mark + '】' + s)
                return 3, ''
            retStr += s
        elif (ivar == -4):
            # y: ...... rule // 注释 // 数据项标识: ruleExpr ; 动作表达式
            try:
                s, mark = getDebugMark(_src, item_s[-1])

                # 去掉//后的注释
                status, rule0_ = StrUtils.getStatementContent(s, getComment=False, valNone=False, separator='//')

                status, rule0 = StrUtils.getStatementContent(rule0_, getComment=False, valNone=False, separator=';')
                status, actionStatement = StrUtils.getStatementContent(rule0_, getComment=True, valNone=False, separator=';')

                plan, rule, final, comment, dataid = RuleTemplate.get_rule(_src, rule0)
                if (rule == ''):
                    logger.error('【' + mark + '】规则（rule）描述错：' + s)
                    return 1, ''
                sr_plan(_src, plan)
                sr_final(_src, final)

                # 如果SRL中没有关于当前项的描述（HTML表头、Dataframe索引），看Rule的定义中是否有这些信息
                status, item_comment = StrUtils.getStatementContent(s, True, False)
                if status:
                    if comment:
                        item_s[-1] += '//' + comment
                        if dataid:
                            item_s[-1] += '//' + dataid
                    else:
                        if dataid:
                            item_s[-1] += s + '//' + dataid
            except (BaseException) as exc:
                logger.error('【' + mark + '】分组（rule）描述错：' + s)
                return 1, ''

            status, s = PandasUtils.dictOrRule2Statements(_src, 'y', indent, item_s, rule,
                                                                actionStatement=actionStatement, rightIsExpr=True)
            if (status != 0):
                return 3, ''
            retStr += s
        elif (ivar == -5):
            # y: ...... dynamic // 注释 // 数据项标识: dynamicExpr ; 动作表达式
            try:
                status, s = processMacros(_src, item_s[-1])
            except (BaseException) as exc:
                logger.error('【' + mark + '】分组（dynamic）描述错：' + s)
                return 1, ''

            # 处理actionStatement，但这里的actionStatement不起作用
            status, actionStatement = processMacros(_src, str(item[-1]), getActionExpr=True)

            actionStatement = 'cu.stateKeyY(_src,' + s + ')'
            retStr += indent + actionStatement + '\n'
        else:
            status, new_statement = processMacros(_src, str(item[-2]))
            g_status -= status
            status, actionStatement = processMacros(_src, str(item[-1]))
            g_status -= status

            actionStatement = ('\n    ' + indent).join(actionStatement.split('\n'))
            retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + actionStatement) + '\n'
    elif isConditionX(item_0):
        ivar, mark = getConditionVar(_src, 'x', item[-2])
        if (ivar >= 0):
            # x: ...... x1 // 注释 // 数据项标识: 条件表达式 ; 动作表达式
            if (ivar >= _src.numX):
                _src.numX = ivar + 1

            s, mark = getDebugMark(_src, item_s[:-1])
            _src.titlesX[ivar] = s
            status, actionStatement = StrUtils.getStatementContent(item[-1], getComment=True, valNone=False, separator='//')

            status, actionStatement = processMacros(_src, item[-1], getActionExpr=str(ivar), xy='x')
            if (status == 2):
                action = _src.get_value('action', defValue='')
                if (action):
                    actionStatement = 'cu.actionX(_src,' + str(ivar) + ', ' + action + ')'
                else:
                    logger.error('【' + mark + '】动作表达式未正确设置，请通过: (1)“$action=表达式”设置, 或, (2)通过“条件语句 ; 动作语句”方式设置')
                    return 2, ''
            elif (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''
            elif (status == 3):
                logger.error('【' + mark + '】动作表达式错：' + item[-1] + '\n' + actionStatement)
                return 2, ''
            else:
                actionStatement = ('\n    ' + indent).join(actionStatement.split('\n'))

            status, new_statement = processMacros(_src, str(item[-1]))
            if (new_statement == '_p_a_s_s__'):
                retStr += (indent + 'pass\n')
            else:
                g_status -= status
                retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + actionStatement) + '\n'
        elif (ivar == -2):
            # x: ...... groupby // 注释 // 数据项标识: groupbyExpr ; 动作表达式
            s, mark = getDebugMark(_src, item_s[-1])
            groupby = s

            # 去掉//后的注释
            status, groupby_ = StrUtils.getStatementContent(groupby, getComment=False, valNone=False, separator='//')

            status, groupby = StrUtils.getStatementContent(groupby_, getComment=False, valNone=False, separator=';')
            status, actionStatement = StrUtils.getStatementContent(groupby_, getComment=True, valNone=False, separator=';')
            if (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''

            if not checkIdentifier(groupby):
                logger.error('【' + mark + '】分组（groupby）字段列表描述错：' + groupby)
                return 1, ''

            status, s = PandasUtils.index2Statements('x', indent, item_s, groupby, actionStatement)
            if (status != 0):
                return 3, ''
            retStr += s
        elif (ivar in (-3, -6)):
            # x: ...... dict // 注释 // 数据项标识: dictExpr ; 动作表达式
            # x: ...... range // 注释 // 数据项标识: rangeExpr ; 动作表达式
            if (ivar == -3):
                key_or_range = 'k'
            else:
                key_or_range = 'r'
            dict0, mark = getDebugMark(_src, item_s[-1])

            # 去掉//后的注释
            status, dict0_ = StrUtils.getStatementContent(dict0, getComment=False, valNone=False, separator='//')

            status, dict0 = StrUtils.getStatementContent(dict0_, getComment=False, valNone=False, separator=';')
            status, actionStatement = StrUtils.getStatementContent(dict0_, getComment=True, valNone=False, separator=';')
            if (status == 1):
                logger.error('【' + mark + '】动作表达式错：' + item[-1])
                return 2, ''

            try:
                groupby_dict = eval(dict0)
            except:
                # 检查这个dict后是不是引用的变量
                try:
                    requireDict = _src.get_value('require')
                    # x dict: dictExpr   # dictExpr的值是这时现算出来的
                    s = eval(dict0, requireDict)
                    if isinstance(s, str):
                        status, s = PandasUtils.justProcessMacros(_src, s)
                        if status:
                            logger.error('【' + mark + '】分组（dict）描述错：' + dict0)
                            return 1, ''
                        groupby_dict = eval(s)
                    elif isinstance(s, dict):
                        groupby_dict = s
                    elif isinstance(s, tuple):
                        # 通过这种方式把表达式返回的值是tuple类型
                        # s = eval("name, name.update({'a':1})", requireDict)
                        groupby_dict = None
                        for x in s:
                            if isinstance(x, dict):
                                groupby_dict = x
                                break
                        if not groupby_dict:
                            logger.error('【' + mark + '】分组（dict）描述数据类型错：' + dict0)
                            return 1, ''
                    else:
                        logger.error('【' + mark + '】分组（dict）描述数据类型错：' + dict0)
                        return 1, ''
                except (BaseException) as exc:
                    logger.error('【' + mark + '】分组（dict）描述错：' + dict0)
                    return 1, ''

            status, s = PandasUtils.dictOrRule2Statements('x', indent, item_s, dict0, rightIsExpr=False,
                                                            actionStatement=actionStatement, key_or_range=key_or_range)
            if (status != 0):
                return 3, ''
            retStr += s
        elif (ivar == -4):
            # x: ...... rule // 注释 // 数据项标识: ruleExpr ; 动作表达式
            try:
                s, mark = getDebugMark(_src, item_s[-1])

                # 去掉//后的注释
                status, rule0_ = StrUtils.getStatementContent(s, getComment=False, valNone=False, separator='//')

                status, rule0 = StrUtils.getStatementContent(rule0_, getComment=False, valNone=False, separator=';')
                status, actionStatement = StrUtils.getStatementContent(rule0_, getComment=True, valNone=False, separator=';')

                plan, rule, final, comment, dataid = RuleTemplate.get_rule(_src, rule0)
                if (rule == ''):
                    logger.error('【' + mark + '】规则（rule）描述错：' + s)
                    return 1, ''
                sr_plan(_src, plan)
                sr_final(_src, final)

                # 如果SRL中没有关于当前项的描述（HTML表头、Dataframe索引），看Rule的定义中是否有这些信息
                status, item_comment = StrUtils.getStatementContent(s, True, False)
                if status:
                    if comment:
                        item_s[-1] += '//' + comment
                        if dataid:
                            item_s[-1] += '//' + dataid
                    else:
                        if dataid:
                            item_s[-1] += s + '//' + dataid
            except (BaseException) as exc:
                logger.error('【' + mark + '】规则（rule）描述错：' + s)
                return 1, ''

            status, s = PandasUtils.dictOrRule2Statements(_src, 'x', indent, item_s, rule,
                                                                actionStatement=actionStatement, rightIsExpr=True)
            if (status != 0):
                return 3, ''
            retStr += s
        elif (ivar == -5):
            # x: ...... dynamic // 注释 // 数据项标识: dynamicExpr ; 动作表达式
            logger.error('【' + mark + '】分组（dynamic）错：横轴(X)方向不支持动态Key式条件。')
            return 1, ''
        else:
            status, new_statement = processMacros(_src, str(item[-2]))
            g_status -= status
            status, actionStatement = processMacros(_src, str(item[-1]))
            g_status -= status

            actionStatement = ('\n    ' + indent).join(actionStatement.split('\n'))
            retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + actionStatement) + '\n'
    else:
        status, new_statement = processMacros(_src, str(item[-2]))
        g_status -= status

        s, mark = getDebugMark(_src, item_s[-1])
        if (s[0] == '$'):
            try:
                # 去掉//后的注释
                status, rule0_ = StrUtils.getStatementContent(s, getComment=False, valNone=False, separator='//')

                status, rule0 = StrUtils.getStatementContent(rule0_, getComment=False, valNone=False, separator=';')
                status, actionStatement = StrUtils.getStatementContent(rule0_, getComment=True, valNone=False, separator=';')
                plan, rule, final, comment, dataid = RuleTemplate.get_rule(_src, rule0)
                if (rule == ''):
                    logger.error('【' + mark + '】规则（rule）描述错：' + s)
                    return 1, ''
                sr_plan(_src, plan)
                sr_final(_src, final)

                # 如果SRL中没有关于当前项的描述（HTML表头、Dataframe索引），看Rule的定义中是否有这些信息
                status, item_comment = StrUtils.getStatementContent(s, True, False)
                if status:
                    if comment:
                        item_s[-1] += '//' + comment
                        if dataid:
                            item_s[-1] += '//' + dataid
                    else:
                        if dataid:
                            item_s[-1] += s + '//' + dataid
            except (BaseException) as exc:
                logger.error('【' + mark + '】规则（rule）描述错：' + s)
                return 1, ''

            status, s = PandasUtils.dictOrRule2Statements(_src, 'x', indent+'    ', item_s, rule,
                                                                actionStatement=actionStatement, rightIsExpr=True)
            if (status != 0):
                return 3, ''
            retStr += (indent + 'if ' + new_statement + ':\n' + s + '\n')
        else:
            status, new_statement2 = processMacros(_src, str(item[-1]))
            g_status -= status

            retStr += (indent + 'if ' + new_statement + ':\n' + indent + '    ' + new_statement2) + '\n'

    return g_status, retStr


def sr_id(_src, statement):
    if statement:
        s, mark = getDebugMark(_src, statement)
        _src.sr_ID = s
    return 0, ''

def sr_sql(_src, statement):
    if statement:
        s, mark = getDebugMark(_src, statement)
        _src.set_value('sql', s)
    return 0, ''

def sr_Ytitles(_src, statement):
    if statement:
        s, mark = getDebugMark(_src, statement)
        _src.set_value('Ytitles', shlex.split(s))
    return 0, ''

def sr_Xtitles(_src, statement):
    if statement:
        s, mark = getDebugMark(_src, statement)
        _src.set_value('Xtitles', shlex.split(s))
    return 0, ''

def sr_require(_src, statement):
    if statement:
        statement, mark = getDebugMark(_src, statement)
        requireDict = {}

        # $ require: var = expression   # 这种require不依赖外部，相当于是内部定义了一个全局量
        type = r'^\s*([a-zA-Z_][a-zA-Z_0-9]*)\s*=\s*(.*)$'
        matchObj = re.match(type, statement, re.I)
        if matchObj:
            # abc = this is an expression
            # matchObj.group(1) = matchObj.group(2)
            s = matchObj.group(1)
            req_s = matchObj.group(2)

            status, req_s = PandasUtils.justProcessMacros(_src, req_s)
            if status:
                return 1, req_s

            try:
                requireDict[s] = eval(req_s, {'_src':_src})
            except (BaseException) as exc:
                return 1, ('ERROR:【' + mark + ': ' + '不认识的关键字】 ' + str(exc) + ': ' + statement)

            sr_plan(_src, s + '=' + req_s)
            dict_ = _src.get_value('require')
            if dict_:
                dict_.update(requireDict)
            else:
                _src.set_value('require', requireDict)
        else:
            # 索取角本对外依赖的量，这个量只在生成规则(python)程序时有用
            statement = statement.split(',')
            for s in statement:
                s = s.strip()
                if isinstance(_src.fun_require, dict):
                    req_s = _src.fun_require.get(s, '')
                else:
                    req_s = _src.fun_require(s, '')
                requireDict[s] = req_s
                if isinstance(req_s, str):
                    req_s = StrUtils.safePythonStr(req_s)
                    sr_plan(_src, s + '="""' + req_s + '"""')
                else:
                    sr_plan(_src, s + '=' + str(req_s))
            dict_ = _src.get_value('require')
            if dict_:
                dict_.update(requireDict)
            else:
                _src.set_value('require', requireDict)
    return 0, ''

def sr_action(_src, statement):
    if statement:
        status, new_statement = processMacros(_src, str(statement))

        status, s = ImportCode.sr_CheckCode(_src, new_statement)
        if (status != 0):
            return 1

        _src.set_value('action', new_statement)
    return 0, ''

def sr_plan(_src, statement):
    if statement:
        if statement not in _src.planStatements:
            _src.planStatements.append(statement)
    return 0, ''

def sr_main(_src, statement):
    if statement:
        _src.mainStatements.append(statement)
    return 0, ''

def sr_final(_src, statement):
    if statement:
        _src.finalExprs.append(statement)
    return 0, ''

def sr_gfinal(_src, statement):
    if statement:
        _src.gfinalExprs.append(statement)
    return 0, ''

def sr_merge(_src, statement):
    if statement:
        _src.mergeExprs.append(statement)
    return 0, ''

def sr_markit(_src, statement):
    return ActionTemplate.sr_markit(_src, statement)

def sr_error(_src, x, statement):
    statement, mark = getDebugMark(_src, statement)
    return 1, ('ERROR:【' + mark + ': ' + '不认识的关键字】 ' + x + ': ' + statement)


def switch_keyword(x):
    try:
        return {
            '$id': sr_id,               #本SR角本的标识
            '$sql': sr_sql,             #SQL角本，需要有返回的结果集
            '$ytitles': sr_Ytitles,     #行索引的标题
            '$xtitles': sr_Xtitles,     #列索引的标题
            '$require': sr_require,     #$require var1, var2 ...; 告诉统计分析程序这些环境需要准备，比如：把nation字典从系统字典库中取来
            '$action': sr_action,       #X轴动作表达式，缺省动作为把这个表达式的值累加到统计结果中
            '$final': sr_final,         #统计分析结果整理动作表达式
            '$gfinal': sr_gfinal,       #针对计算结果数据总体的整理动作（每次分析只执行一次）
            '$plan': sr_plan,           #放在生成角本的最前面
            '$main': sr_main,           #放在main函数中。所有自由条件：动作组合都是放在main中执行的
            '$merge': sr_merge,
            '$markit': sr_markit,
        }[x.lower()]
    except KeyError:
        return ''


def dict2Py(_src, sr_filename, parameters={}, sr_String=''):
    logger.info('正在装入统计分析角本: ' + sr_filename)

    if isinstance(parameters, str):
        try:
            parameters = eval(parameters)
        except (BaseException) as exc:
            return 1, '调用StatisticsReporter时参数parameters写法不符合要求，不能转换成python的dict或list数据类型'

    if (sr_String):
        sr_file = sr_String.format(**parameters)
    else:
        status, sr_file = StrUtils.readFile(sr_filename, parameters=parameters)
        if status:
            logger.error(sr_file)
            return 1, ''

    if not sys.warnoptions:
        import warnings
        warnings.simplefilter("ignore")

    try:
        content = sr_yaml.load(sr_file, Loader=sr_yaml.BaseLoader, debug=_src.debug)
    except (sr_yaml.YAMLError) as exc:
        return 1, 'SRL角本错: ' + str(exc)

    if not isinstance(content, dict):
        return 1, 'SRL角本格式错: ' + str(content)

    lastItem = []

    retStr = '''import CommonUtils as cu
{__action__plan__src_planStatements__}
def main(_src, **kargs):
    pass
{__action__plan__src_mainStatements__}'''

    retStrX = ''
    retStrY = ''

    g_status = 0            #累积性错误记录
    if (content):
        for item in dict_generator(content):
            item0 = item[0]

            if (item0.startswith('$') and len(item) <= 2 and not item0.startswith('$.')):
                if (item0 == '$'):
                    # item[0] = '$' not item[0] = '$action'
                    s = item[1]
                    s, mark = getDebugMark(_src, s)
                    for i in range(len(s)):
                        if (s[i] != '.'):
                            s = (' ' * i) + s[i:]
                            break;

                    # 因为冒号在sr_yaml中有特别意思，所以python需要的冒号，写成中文全角冒号'：'
                    s = s.replace('：', ':')

                    # 加上python语句“def”（函数定义）尾部的冒号
                    if s.startswith('def '):
                        if not s.endswith(':'):
                            s = s + ':'

                    retStr += s + '\n'
                else:
                    pos, s = StrUtils.getToken(item0, 0)
                    pos, s1 = StrUtils.getToken(item0, pos)
                    if (pos >= 0):
                        s = '$' + s1

                    action = switch_keyword(s)
                    if action:
                        status, info = action(_src, item[1])
                        if (status != 0):
                            logger.error(info)
                            return 3, ''
                    else:
                        status, info = sr_error(_src, s, item[1])
                        logger.error(info)
                        return 3, ''
            else:
                item_0, mark = getDebugMark(_src, item0)

                if isConditionY(item_0):
                    item_0_xy = 'y'
                    item = item[1:]
                    i = itemCmp(lastItem, item)
                    indent = '    ' * (i + 1)
                    lastItem = item[0:-2]
                elif isConditionX(item_0):
                    item_0_xy = 'x'
                    item = item[1:]
                    i = itemCmp(lastItem, item)
                    indent = '    ' * (i + 1)
                    lastItem = item[0:-2]
                else:
                    item_0_xy = 'z'
                    i = 0
                    indent = '    '
                    lastItem = []

                status, s = printIitems(_src, item_0, item, item[i:], indent)
                if (status > 0):
                    return 3, ''
                if (status < 0):
                    g_status += status

                if (item_0_xy != 'z'):
                    if (item_0_xy == 'y'):
                        retStrY += s
                    else:
                        retStrX += s
                else:
                    retStr += s

        _src.init_datas()
    else:
        retStr += '''    print ('Empty main() function')\n'''

    # 生成动作表达式执行需要的准备工作，plan
    no_indent_ = ''
    for s in _src.planStatements:
        no_indent_ += s + '\n'
    if (no_indent_ == ''):
        no_indent_ = '\n'

    indent_ = ''
    for s in _src.mainStatements:
        indent_ += '    ' + '\n    '.join(s.split('\n')) + '\n'
    if (indent_ == ''):
        indent_ = '\n'

    retStr = retStr.format(__action__plan__src_planStatements__ = no_indent_,
                           __action__plan__src_mainStatements__ = indent_)

    if retStrY:
        retStr += '''\ndef mainY(_src, df):\n''' + retStrY
    else:
        retStr += '''\ndef mainY(_src, df):\n    return 1\n'''

    if retStrX:
        retStr += '''\ndef mainX(_src, df):\n''' + retStrX
    else:
        retStr += '''\ndef mainX(_src, df):\n    return 1\n'''

    # 生成动作表达式执行需要的清理工作，final，生成到函数final()，角本执行“结束”后调设程序调用它
    retStr += '''\ndef final(_src):\n'''
    if (len(_src.finalExprs) > 0):
        for s in _src.finalExprs:
            retStr += '    ' + '\n    '.join(s.split('\n')) + '\n'
    else:
        retStr += '    pass\n'

    retStr += '''\ndef gfinal(_src):\n'''
    if (len(_src.gfinalExprs) > 0):
        for s in _src.gfinalExprs:
            retStr += '    ' + '\n    '.join(s.split('\n')) + '\n'
    else:
        retStr += '    pass\n'

    retStr += '''\ndef merge(_src):\n'''
    if (len(_src.mergeExprs) > 0):
        for s in _src.mergeExprs:
            retStr += '    ' + '\n    '.join(s.split('\n')) + '\n'
    else:
        retStr += '    pass\n'

    return g_status, retStr
